package com.softwareengineering.trafficapplicationv2;
import java.util.Random;
import java.util.Vector;

import android.annotation.SuppressLint;
import android.graphics.Color;

/**
 * This file is part of the Traffic Simulation Application.
 * The Traffic Simulation Application is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * The Traffic Simulation Application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with the Traffic Simulation Application.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * @file Microstreet.java
 * @author (c) Martin Treiber, http://traffic-simulation.de/
 * @alterations David Black, Chris Nelson, and Garrett Stibbs, (c) December 03, 2012
 * @description Representation of a two-lane road section for one direction.
 * 				The main elements of MicroStreet are street, a vector of Moveable's
 * 				representing the vehicles, the update method invoked in every time step.
 * 				Among others, it calls all methods mentioned below. Methods for moving the
 * 				vehicles (translate), accelerating them (accelerate) and performing the
 * 				lane changes (changeLanes). A sorting routine sort for rearranging the
 * 				vehicle order in street in the order of decreasing longitudinal positions.
 * 				The method ioFlow implementing the upstrea and downstream boundary conditions
 * 				(inflow and outflow). The realization of an on ramp, the class Onramp, is
 * 				derived from this class.
 * 
 */

public class MicroStreet implements Constants {

	public interface ScoreChangeListener {
		public void onScoreChange(int score);
	}

	private ScoreChangeListener listener;
	private int score = 0;

	static final int LEFT=0; 
	static final int RIGHT=1; 

	// colors
	static final int colorCar= Color.rgb(210,0,0);
	static final int colorTruck= Color.rgb(80,80,85);
	static final int colorPerturb= Color.rgb(255,160,160);

	// vector of Moveables
	final int IMAXINIT=100;
	@SuppressWarnings("rawtypes")
	protected Vector street = new Vector(IMAXINIT);

	// vector of data of Moveables for output (only model is missing)
	@SuppressWarnings("rawtypes")
	public Vector positions =  new Vector(IMAXINIT);
	@SuppressWarnings("rawtypes")
	public Vector velocities =  new Vector(IMAXINIT);
	@SuppressWarnings("rawtypes")
	public Vector numbers = new Vector(IMAXINIT);
	@SuppressWarnings("rawtypes")
	public Vector lanes = new Vector(IMAXINIT);
	@SuppressWarnings("rawtypes")
	public Vector realLanes = new Vector(IMAXINIT);
	@SuppressWarnings("rawtypes")
	public Vector colors =new Vector(IMAXINIT);
	@SuppressWarnings("rawtypes")
	public Vector lengths=new Vector(IMAXINIT);

	// additional vectors for output
	@SuppressWarnings("rawtypes")
	public Vector distances = new Vector(IMAXINIT);
	@SuppressWarnings("rawtypes")
	public Vector old_pos = new Vector(IMAXINIT);
	@SuppressWarnings("rawtypes")
	public Vector old_numbers = new Vector(IMAXINIT);

	// floating car data
	public double fcd = 0.0;	// distance
	public double fcvd = 0.0;	// approaching rate
	public double fcvel = 0.0;	// v
	public double fcacc = 0.0;	// acceleration
	public int fcnr=0;
	public boolean red=false;

	// longitudinal models; 
	protected MicroModel idmCar =new IDMCar();
	protected MicroModel idmTruck=new IDMTruck();
	protected MicroModel sync1Car;
	protected MicroModel sync2Car;
	protected MicroModel sync1Truck;
	protected MicroModel sync2Truck;

	// lane-change models  (p, db, smin, bsave)

	//!!! truck (=> vw for better impl!)
	protected LaneChange polite=new LaneChange(P_FACTOR_TRUCK, 
			DB_TRUCK, MAIN_SMIN,MAIN_BSAVE, BIAS_RIGHT_TRUCK);

	//!!! car (=> vw for better impl!)
	protected LaneChange inconsiderate=new LaneChange(P_FACTOR_CAR, 
			DB_CAR, MAIN_SMIN,MAIN_BSAVE, BIAS_RIGHT_CAR);

	// neighbours
	protected Moveable vL,vR,hL,hR;

	// can be modified interactively
	protected Random random=new Random(42); // for truck perc. and veh. numbers
	protected int choice_Szen;
	protected double roadLength; // length depends on choice_Szen 
	protected double uppos; // location where flow-conserving bottleneck begins

	// time and simulation control
	protected double time;

	// Source: nin = integral of inflow mod 1
	protected double nin=0.0;

	protected double tl;
	@SuppressWarnings("unchecked")
	public MicroStreet(double length, double density, 
			double p_factor, double deltaB,
			int floatcar_nr, int choice_Szen, ScoreChangeListener listener){
		this.listener = listener;
		time=0;
		roadLength = length;
		uppos = 0.5*roadLength;
		this.choice_Szen = choice_Szen;
		inconsiderate.set_p(p_factor);
		inconsiderate.set_db(deltaB);
		double mult=1;
		double bias_right_truck=(choice_Szen==LANE_CLOSURE) ? BIAS_RIGHT_TRUCK3 : BIAS_RIGHT_TRUCK;
		double bias_right_car=(choice_Szen==LANE_CLOSURE) ? BIAS_RIGHT_CAR3 : BIAS_RIGHT_CAR;
		polite.set_biasRight(mult*bias_right_truck);
		inconsiderate.set_biasRight(mult*bias_right_car);
		fcnr = floatcar_nr;

		//######### Four Way

		if (choice_Szen==FOUR_WAY){
			double pos = roadLength*tl;
			street.addElement (new Obstacle(pos, 0, 10., 0));
			street.addElement (new Obstacle(pos, 1, 10., 1));
			street.addElement
			(new Car(5.0,idmCar.Veq(roadLength),0, idmCar, polite, 
					PKW_LENGTH_M, colorCar, 2));
			
		}
		// ######## Traffic Light
		else if (choice_Szen==TRAFFIC_LIGHT){
			
			double pos = roadLength*RELPOS_TL;
			street.addElement (new Obstacle(pos, 0, 10., 0));
			street.addElement (new Obstacle(pos, 1, 10., 1));
			//street.addElement
			//(new Car(5.0,idmCar.Veq(roadLength),0, idmCar, polite, 
			//		PKW_LENGTH_M, colorCar, 2));
			
			double pos2 = roadLength*RELPOS_TL2;
			street.addElement (new Obstacle(pos2, 0, 10., 0));
			street.addElement (new Obstacle(pos2, 1, 10., 1));
			street.addElement
			(new Car(5.0,idmCar.Veq(roadLength),0, idmCar, polite, 
					PKW_LENGTH_M, colorCar, 2));
		}
		//#########  Lane closure on (left) lane 0

		if (choice_Szen==LANE_CLOSURE){
			double bottleneck_end = RELPOS_LANECL*roadLength;
			double obst_elem_length = 12;
			int    n_obst            = (int)(LANECL_LENGTH);
			for (int i=0; i<n_obst; i++){
				double pos = bottleneck_end - i*obst_elem_length; 
				street.insertElementAt
				(new Obstacle(pos, 0, obst_elem_length+1, i), i);
			}
			street.insertElementAt
			(new Car(5.0, idmTruck.Veq(roadLength), 0, idmTruck, polite, 
					LKW_LENGTH_M, colorTruck, n_obst), n_obst);
		}

		else if (choice_Szen==UPHILL){
			sync1Car  = new IDMSync();
			sync2Car  = new IDMSyncdown(); // bottleneck region
			sync1Truck= new IDMTruckSync();
			sync2Truck= new IDMTruckSyncdown(); // bottleneck region

			street.insertElementAt
			(new Car(5.0, sync1Car.Veq(roadLength), 0, sync1Car, polite, 
					PKW_LENGTH_M, colorCar, 0), 0);
		}
	}
	
	protected void setTL(double tl){
		this.tl=tl;
	}

	protected void accelerate(double dt){
		// the first and last cars on each lane 
		// are virtual BCCcars such that lanecHange(), accelerate() are always defined
		int imax=street.size()-2; 

		// floating car data
		fcd=0.0; 
		fcvd=0.0;
		fcvel=0.0;
		fcacc=0.0;

		// Counting loop goes backwards to implement parallel update!
		for (int i=imax-1; i>=2; i--){
			Moveable me = (Moveable)street.elementAt(i);
			int lane = me.lane();
			int act_nr = me.NR();
			int next_ind = nextIndexOnLane(lane, i);
			Moveable frontVeh = (Moveable)street.elementAt(next_ind);

			// if actual car = floating car, gather "detector data"
			if (act_nr==fcnr){ 
				fcd   = frontVeh.position()-me.position();
				fcvd  = frontVeh.velocity()-me.velocity();
				fcvel = me.velocity();
				me.accelerate(dt, frontVeh);
				double vNew=me.velocity();
				fcacc = (vNew-fcvel)/dt;
			}

			// Otherwise, do just the acceleration
			else {
				me.accelerate(dt,  frontVeh);
			}
		}
	}

	protected void adaptToNewDensity(double density, double perTr){
		int nCars_wished=(int)(density*roadLength*2.0);
		int nCars = positions.size();
		
		if (nCars_wished > nCars) insertOneVehicle(perTr);
		if (nCars_wished < nCars) removeOneVehicle();
	}
	
	public void applyLocalPerturbation(){
		int imax=street.size();
		
		// the first and last two cars are BCCcars on each lane during acceleration()
		// but not during application of this function from further above
		int i=0+(5*(imax-0))/6; 
		Moveable veh=((Moveable)(street.elementAt(i)));
		double vel=0.1*veh.velocity();
		
		veh.setVelocity(vel);
		veh.setColor(colorPerturb);
	}

	// make actual state available in form of vectors over all vehicles;
	// protected methods set public vectors to be used for graphical output
	protected void changeLanes(double dt){
		int imax=street.size()-3;

		for (int i=2; i<imax; i++){
			Moveable me = (Moveable)(street.elementAt(i));

			if (me.timeToChange(dt)){
				int lane = me.lane();
				int newLane = ((lane==0) ? 1 : 0);
				setNeighbours(i); // -> vR, hR, vL, hL
				Moveable fOld = (lane==0) ? vL : vR; // front vehicle own lane
				Moveable fNew = (lane==0) ? vR : vL; // front vehicle new lane
				Moveable bNew = (lane==0) ? hR : hL; // back vehicle new lane

				// do actual change if incentive criterion fulfilled
				// setLane method of Moveable; setLanes M. of MicroStreet!

				if (me.change (fOld, fNew, bNew)) ((Moveable)(street.elementAt(i))).setLane(newLane);
			}
		}
	}

	protected void clearBCCars(){
		street.removeElementAt(0);
		street.removeElementAt(0);
		
		int imax=street.size();
		imax--;
		
		street.removeElementAt(imax);
		imax--;
		
		street.removeElementAt(imax);
	}

	protected int firstIndexOnLane(int lane) {
		int nr_max=(street.size())-1;
		int i=0;
		boolean carFound = false;
		if (nr_max>=0){
			while ((i<=nr_max) && (!carFound)){
				if (((Moveable)street.elementAt(i)).lane()==lane) carFound = true;
				i++;
			}
		}
		
		return ((carFound) ? i-1 : -1);
	}

	@SuppressWarnings("unchecked")
	protected void insertBCCars(int choice_BC) {
		// virtual cars so that acceleration, lanechange etc 
		// always defined (they need in general all next neighbors)

		int i_rd=firstIndexOnLane(1);  // index right downstream vehicle
		int i_ld=firstIndexOnLane(0);  // ...

		int i_ru=lastIndexOnLane(1);
		int i_lu=lastIndexOnLane(0);

		double upLeftPos=(i_lu>-1) 
				? ((Moveable)(street.elementAt(i_lu))).position():0;
		double upRightPos=(i_ru>-1) 
				? ((Moveable)(street.elementAt(i_ru))).position():0;    
		double upLeftVel=(i_lu>-1) 
				? ((Moveable)(street.elementAt(i_lu))).velocity():0;    
		double upRightVel=(i_ru>-1) 
				? ((Moveable)(street.elementAt(i_ru))).velocity():0;

		double downLeftPos=(i_ld>-1) 
				? ((Moveable)(street.elementAt(i_ld))).position():roadLength;
		double downRightPos=(i_rd>-1) 
				? ((Moveable)(street.elementAt(i_rd))).position():roadLength; 
		double downLeftVel=(i_ld>-1) 
				? ((Moveable)(street.elementAt(i_ld))).velocity():0;    
		double downRightVel=(i_rd>-1) 
				? ((Moveable)(street.elementAt(i_rd))).velocity():0;

		double dx = 200.; // distance of the boundary cars
		street.insertElementAt(new BCCar(downLeftPos + dx, downLeftVel, 0,
				idmCar, PKW_LENGTH_M), 0);
		street.insertElementAt(new BCCar(downRightPos + dx, downRightVel,
				1, idmCar, PKW_LENGTH_M), 0);
		
		int imax = street.size();
		street.insertElementAt(new BCCar(upLeftPos - dx, upLeftVel, 0,
				idmCar, PKW_LENGTH_M), imax);
		imax = street.size();
		street.insertElementAt(new BCCar(upRightPos - dx, upRightVel, 1,
				idmCar, PKW_LENGTH_M), imax);
	}

	@SuppressWarnings("unchecked")
	private void  insertOneVehicle(double perTr) {
		int nveh=positions.size();
		final double mingap=10.;
		double maxgap=0.;
		int i_maxgap=0;
		double pos_maxgap; // position of vehicle which maxgap in front
		int lane_maxgap; // lane of vehicle which maxgap in front
		int nleft=0;
		int nright=0;
		
		for (int i=0; i<nveh; i++) {
			Moveable me = (Moveable)(street.elementAt(i));
			if (me.lane()==LEFT) nleft++;
			else nright++;

			double gap = ((Double)distances.elementAt(i)).doubleValue();
			if (gap>maxgap) {
				maxgap=gap; 
				i_maxgap=i;
			}
		}

		if (nleft<2) {
			maxgap=roadLength;
			pos_maxgap = 0;
			lane_maxgap =LEFT;
			i_maxgap=nveh;
		}
		else if (nright<2){
			maxgap=roadLength;
			pos_maxgap = 0;
			lane_maxgap = RIGHT;
			i_maxgap=nveh;
		}
		else{
			pos_maxgap = ((Double) positions.elementAt(i_maxgap)).doubleValue();
			lane_maxgap = ((Integer) lanes.elementAt(i_maxgap)).intValue();
		}

		// insert vehicle if sufficient gap
		if (maxgap>mingap){
			double rand = random.nextDouble()*1.0;
			int randInt = Math.abs(random.nextInt());
			MicroModel modelNew = (rand<perTr) ? idmTruck :idmCar;
			LaneChange changemodelNew = (rand<perTr) 
					? polite : inconsiderate;
			double posNew = pos_maxgap + 0.5 * maxgap;
			double vNew = modelNew.Veq(0.5*maxgap);
			double lNew = (rand<perTr) ? LKW_LENGTH_M : PKW_LENGTH_M;
			int colorNew  = (rand<perTr) 
					? colorTruck : colorCar;
			
			street.insertElementAt
			((new Car(posNew, vNew, lane_maxgap, 
					modelNew, changemodelNew, 
					lNew, colorNew, randInt)),i_maxgap);
		}

	}

	@SuppressWarnings("unchecked")
	protected void ioFlow(double dt, double qIn, double perTr, 
			int choice_BC){
		// open BC
		int imax=street.size()-1;
		final double spaceFree=200.;  // v0=ve(spaceFree)
		final double spaceMin=27; // minimum headway for new vehicle

		// outflow: 
		// just remove; 
		// insertBCCars always guarantees for virtual front vehicles

		if (imax>=0){
			while ( (imax>=0) &&  // (imax>=0) first to prevent indexOutOf...!
					( ((Moveable)(street.elementAt(0))).position()>roadLength)){
				street.removeElementAt(0);
				imax--;
				if (listener != null) listener.onScoreChange(++score); //update score
			}
		}

		// inflow
		nin=nin+qIn*dt;

		if (nin>1.0){  // new vehicle imposed by the inflow BC
			nin=nin-1.0;
			int i_lu=lastIndexOnLane(0);  // lu = "left upper"
			int i_ru=lastIndexOnLane(1);

			// at least 1 vehicle on either lane
			if ((i_lu>=0) && (i_ru>=0)) {  
				imax=street.size()-1;
				int laneLastVeh = 
						((Moveable)(street.elementAt(imax))).lane();
				int lane  = (laneLastVeh ==0) ? 1 : 0;  // insert on other lane
				int iPrev = lastIndexOnLane(lane); // index of previous vehicle
				double space =((Moveable)(street.elementAt(iPrev))).position();

				// enough space for new vehicle to enter? (!red)
				if (!(red=(space<spaceMin))) {
					MicroModel carmodel  =(choice_Szen!=UPHILL) 
							? idmCar : sync1Car;
					MicroModel truckmodel=(choice_Szen!=UPHILL) 
							? idmTruck : sync1Truck;
					double rand = random.nextDouble()*1.0;
					int randInt = Math.abs(random.nextInt());
					MicroModel modelNew  = (rand<perTr) 
							? truckmodel : carmodel;
					LaneChange changemodelNew = (rand<perTr) 
							? polite : inconsiderate;
					double vNew = modelNew.Veq(space);
					double lNew = (rand<perTr) ? LKW_LENGTH_M : PKW_LENGTH_M;
					int colorNew  = (rand<perTr) 
							? colorTruck : colorCar;

					imax=street.size();
					street.insertElementAt
					(new Car(0.0, vNew, lane, modelNew, changemodelNew, 
							lNew, colorNew, randInt),imax);
				}
			}

			// at least one lane without vehicles
			else {  
				MicroModel carmodel =(choice_Szen!=UPHILL) ? idmCar : sync1Car;
				MicroModel truckmodel=(choice_Szen!=UPHILL) ? idmTruck : sync1Truck;
				double rand = random.nextDouble()*1.0;
				int randInt = Math.abs(random.nextInt());
				MicroModel modelNew  = (rand<perTr) ? truckmodel : carmodel;
				double vNew = modelNew.Veq(spaceFree);
				double lNew = (rand<perTr) ? LKW_LENGTH_M : PKW_LENGTH_M;
				int colorNew = (rand<perTr) ? colorTruck : colorCar;
				int lane=(i_lu<0) ? 0 : 1;
				imax=street.size();
				street.insertElementAt 
				(new Car(0.0, vNew, lane, modelNew,
						inconsiderate, lNew, colorNew, randInt),imax);
			}
		}
	}

	protected int lastIndexOnLane(int lane){

		int nr_max=(street.size())-1;
		int i=nr_max;
		boolean carFound = false;
		if (nr_max>=0){
			while ((i>=0) && (!carFound)){
				if (((Moveable)street.elementAt(i)).lane()==lane){
					//  if (((((Moveable)street.elementAt(i)).lane())==lane)&&(flag==0)){
					carFound = true;
				}
				i--;
			}
		}
		return ((carFound) ? i+1 : -1);
	}

	public double length(){ return roadLength; }


	// !! bounds not checked
	protected int nextIndexOnLane(int lane, int ind){
		int next_ind=ind-1;
		while ((((Moveable)street.elementAt(next_ind)).lane())!=lane) next_ind--;
		return next_ind;
	}


	// HIER truck => car implementieren!!
	protected void open(int vPos){
		street.removeElementAt(vPos);
		
	}

	// !! bounds not checked
	protected int prevIndexOnLane(int lane, int ind){
		int next_ind=ind+1;
		while ((((Moveable)street.elementAt(next_ind)).lane())!=lane) next_ind++;
		return next_ind;
	}

	private void  removeOneVehicle(){
		int  indexToRemove = Math.abs(random.nextInt()) % (positions.size());
		street.removeElementAt(indexToRemove);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected Vector setColors(){
		Vector temp=new Vector(IMAXINIT);
		int imax=street.size();
		for (int i=0; i<imax; i++){
			int c =((Moveable)(street.elementAt(i))).color();
			temp.insertElementAt(c, i);
		}
		return temp;
	}

	@SuppressLint("UseValueOf")
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected Vector setDistances(){  // neglect gaps in front of first/last veh on either lane (i<=iFrontCarsBoth)
		Vector temp  = new Vector(IMAXINIT);
		
		int    imax = street.size();
		int iFirstLeft = firstIndexOnLane(0);
		int iFirstRight = firstIndexOnLane(1);
		int iFrontCarsBoth = (iFirstLeft>iFirstRight) 
				? iFirstLeft : iFirstRight;
		
		for (int i=0; i<= iFrontCarsBoth+1; i++) temp.insertElementAt(new Double(-1.), i);
		
		for (int i=iFrontCarsBoth+1; i<imax; i++){
			int lane = ((Integer) lanes.elementAt(i)).intValue();
			int iFront = nextIndexOnLane(lane, i);
			double distance = 
					((Double) positions.elementAt(iFront)).doubleValue()
					- ((Double) positions.elementAt(i)).doubleValue();
			temp.insertElementAt(new Double(distance), i);
		}
		
		return temp;
	}

	@SuppressWarnings("rawtypes")
	@SuppressLint("UseValueOf")
	protected Vector setLanes(){
		Vector temp=new Vector(IMAXINIT);
		int imax=street.size();
		
		for (int i=0; i<imax; i++){
			int lane =((Moveable)(street.elementAt(i))).lane();
			extracted(temp, i, lane);
		}
		
		return temp;
	}

	@SuppressLint("UseValueOf")
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void extracted(Vector temp, int i, int lane) {
		temp.insertElementAt(new Integer(lane), i);
	}

	public void setLength(double roadLength) {
		this.roadLength = roadLength;
	}

	@SuppressLint("UseValueOf")
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected Vector setLengths(){
		Vector temp=new Vector(IMAXINIT);
		int imax=street.size();
		
		for (int i=0; i<imax; i++){
			Double Len =new Double(((Moveable)(street.elementAt(i))).length());
			temp.insertElementAt(Len, i);
		}
		
		return temp;
	}

	protected void setNeighbours(int ind) {
		int vl=nextIndexOnLane(0, ind);
		int vr=nextIndexOnLane(1, ind);
		int hl=prevIndexOnLane(0, ind);
		int hr=prevIndexOnLane(1, ind);

		vL = (Moveable) street.elementAt(vl);
		vR = (Moveable) street.elementAt(vr);
		hL = (Moveable) street.elementAt(hl);
		hR = (Moveable) street.elementAt(hr);
	}

	// Sort to decreasing values of pos using the bubblesort algorithm;
	// Pairwise swaps running over all vehicles; 
	// repeat loop over vehicles until
	// sorted; typically, only 2 runs over the loop are needed
	// (one to sort; one to check)
	@SuppressWarnings("rawtypes")
	protected Vector setNr(){
		Vector temp=new Vector(IMAXINIT);
		int imax=street.size();
		
		for (int i=0; i<imax; i++){
			int nr =((Moveable)(street.elementAt(i))).NR(); 
			extracted(temp, i, nr);
		}
		
		return temp;
	}

	// returns index of first (most downstream) vehicle on given lane;
	// if no vehicles on this lane; -1 is returned
	@SuppressLint("UseValueOf")
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected Vector setPos(){
		Vector temp = new Vector(IMAXINIT);
		int imax = street.size();
		
		for (int i=0; i<imax; i++){
			double pos= ((Moveable)(street.elementAt(i))).position();
			temp.insertElementAt( new Double(pos), i);
		}
		
		return temp;
	}

	// returns index of most upstream vehicle on given lane
	@SuppressLint("UseValueOf")
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected Vector setRealLanes() {
		Vector temp=new Vector(IMAXINIT);
		int imax=street.size();
		
		for (int i=0; i<imax; i++){
			double realLane =((Moveable)(street.elementAt(i))).realLane();
			temp.insertElementAt(new Double(realLane), i);
		}
		
		return temp;
	}

	@SuppressLint("UseValueOf")
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected Vector setVel() {
		Vector temp=new Vector(IMAXINIT);
		int imax=street.size();
		
		for (int i=0; i<imax; i++){
			double vel =((Moveable)(street.elementAt(i))).velocity(); 
			temp.insertElementAt(new Double(vel), i);
		}
		
		return temp;
	}

	@SuppressWarnings("unchecked")
	protected void sort() { 
		boolean sorted=false;

		while (!sorted){
			sorted = true;
			int imax = street.size();
			for (int i=1; i<imax; i++) {
				double p_back=((Moveable)street.elementAt(i)).position();
				double p_front=((Moveable)street.elementAt(i-1)).position();
				if (p_back>p_front) {
					sorted=false;
					Moveable temp=(Moveable) street.elementAt(i-1);
					street.setElementAt((Moveable) street.elementAt(i), i-1);
					street.setElementAt(temp,(i));
				}
			}	    
		}
	}

	// !! assumed that neighbors are existent; otherwise OutOfBoundsException
	protected int translate(double dt, int choice_Szen) {
		// without sorting
		int imax=street.size();
		
		for (int i=0; i<imax; i++){
			Moveable me  = (Moveable)street.elementAt(i);
			double x_old = me.position();
			me.translate(dt);
			double x_new = me.position();

			// if Scenario 4, flow-conserving bottlenecks 
			// realized with parameter
			// gradients and uppos is crossed: 
			// Change upstream to downstream parameters
			if (choice_Szen==UPHILL) {
				if ((x_old<=uppos)&&(x_new>uppos)){
					if (me.model()==sync1Truck){
						me.setModel(sync2Truck);	
					}
					else{
						me.setModel(sync2Car);
					}
				}
			}
		}
		
		return street.size();
	}

	// remove the two white obstacles when traffic light turns green
	// (only once in a simulation and only for choice_Szen==5)
	public void update(double time, double dt, int choice_Szen,
			double density, double qIn, double perTr, 
			double p_factor, double deltaB){

		this.time=time;

		old_pos=setPos();	// need old info for detectors and drawing
		old_numbers=setNr();  
		
		if (choice_Szen==LANE_CLOSURE){	// particularly aggressive for lane closing
			inconsiderate.set_p(0.);
			polite.set_db(DB_TRUCK3);
			inconsiderate.set_db(DB_CAR3);
			inconsiderate.set_bsave(6.);
		}
		else{
			inconsiderate.set_p(p_factor);
			polite.set_db(DB_TRUCK);
			inconsiderate.set_db(DB_CAR);
		}

		int choice_BC = 1;

		// Main action: accelerate, changeLanes, translate, sort!
		// At least one vehicle must be on each lane
		insertBCCars(choice_BC);   // virtual boundary cars
		accelerate(dt);
		changeLanes(dt);
		clearBCCars();
		translate(dt, choice_Szen);
		sort();

		positions=setPos();
		velocities=setVel();
		numbers=setNr();
		lanes=setLanes();
		realLanes=setRealLanes();
		colors=setColors();
		lengths=setLengths();
		distances = setDistances();

		// source terms !! export of positions etc before such that
		// old_positions and positions etc must 
		// always have same vehicle number!! 
		ioFlow(dt, qIn, perTr, choice_BC); //needs positions etc
	}
}
